﻿using System;
using System.Collections.Generic;
using Gdi = System.Drawing;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace iNet.Media.Imaging.Filters
{
	/// <summary>
	/// 旋轉濾鏡工作。
	/// </summary>
	public class RotateFilterTask : ImageFilterTask
	{
		#region 欄位
		double _Angle;
		Color _BackgroundColor = Colors.Transparent;
		bool _PreserveImageSize;
        #endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 RotateFilterTask 執行個體。
		/// </summary>
		public RotateFilterTask()
		{ }
		/// <summary>
		/// 初始化新的 RotateFilterTask 執行個體。
		/// </summary>
		/// <param name="sourceBitmap">來源點陣圖。</param>
		/// <param name="angle">順時鐘方向的旋轉角度。</param>
		public RotateFilterTask(IBitmapImage sourceBitmap, double angle)
			: base(sourceBitmap)
		{
			_Angle = angle;
		}
        #endregion
		#region Angle
		/// <summary>
		/// 取得或設定順時鐘方向的旋轉角度，預設值為 0。
		/// </summary>
		public double Angle
		{
			get
			{
				return _Angle;
			}
			set
			{
				this.SetInitializingParameter<double>(ref _Angle, value);
			}
		}
		#endregion
		#region ApplyFilter
		/// <summary>
		/// 套用濾鏡。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="sourceImage">欲套用濾鏡的影像。</param>
		/// <param name="resultImage">已套用濾鏡的影像。</param>
		/// <returns>True 表示濾鏡套用成功，False 表示失敗。</returns>
		protected override bool ApplyFilter(Tasks.TaskVersion version, IImage sourceImage, out IImage resultImage)
		{
			//初始化參數
			resultImage = null;

			//取得資料
			double angle;
			Color backColor;
			bool preserveImageSize;
			if (!this.ReadValue<double>(version, ref _Angle, out angle)
				|| !this.ReadValue<Color>(version, ref _BackgroundColor, out backColor)
				|| !this.ReadValue<bool>(version, ref _PreserveImageSize, out preserveImageSize))
			{
				return true;
			}

			//修正角度範圍 (-180 ~ 180)
			if (angle == 0)
			{
				resultImage = (sourceImage.Clone() as IBitmapImage);
				return true;
			}
			angle %= 360;
			if (angle > 180)
				angle -= 360;
			else if (angle < -180)
				angle += 360;

			//直接旋轉
			if (!_PreserveImageSize && (angle == 90 || angle == -90 || angle == 180 || angle == -180))
			{
				TransformedBitmap resultBitmapSource = new TransformedBitmap(sourceImage.ToBitmapSource(), new RotateTransform(angle));
				resultImage = new BitmapImage(sourceImage.MediaStorage, resultBitmapSource, true, sourceImage.Metadata, sourceImage.RawFormat);
				return true;
			}

			//計算目標寬高
			IBitmapImage sourceBitmap = (sourceImage as IBitmapImage);
			int targetWidth, targetHeight;
			Gdi.PointF[] gdiDrawingPoints = null;
			if (angle == 90 || angle == -90)
			{
				if (!_PreserveImageSize)
				{
					targetWidth = sourceBitmap.PixelHeight;
					targetHeight = sourceBitmap.PixelWidth;
				}
				else
				{
					targetWidth = sourceBitmap.PixelWidth;
					targetHeight = sourceBitmap.PixelHeight;
					int sideDiff = ((sourceBitmap.PixelWidth - sourceBitmap.PixelHeight) / 2);
					if (angle == 90)
					{
						gdiDrawingPoints = new Gdi.PointF[]{
							new Gdi.PointF((sourceBitmap.PixelWidth - sideDiff), -sideDiff),
							new Gdi.PointF((sourceBitmap.PixelWidth - sideDiff), (sourceBitmap.PixelHeight + sideDiff)),
							new Gdi.PointF(sideDiff, -sideDiff),
						};
					}
					else
					{
						gdiDrawingPoints = new Gdi.PointF[]{
							new Gdi.PointF(sideDiff, (sourceBitmap.PixelHeight + sideDiff)),
							new Gdi.PointF(sideDiff, -sideDiff),
							new Gdi.PointF((sourceBitmap.PixelWidth - sideDiff), (sourceBitmap.PixelHeight + sideDiff)),
						};
					}
				}
			}
			else if (angle == 180 || angle == -180)
			{
				targetWidth = sourceBitmap.PixelWidth;
				targetHeight = sourceBitmap.PixelHeight;
			}
			else
			{
				double radius = (angle / 180.0 * Math.PI);
				double sinWidth = (sourceBitmap.PixelWidth * Math.Sin(radius));
				double cosWidth = (sourceBitmap.PixelWidth * Math.Cos(radius));
				double sinHeight = (sourceBitmap.PixelHeight * Math.Sin(radius));
				double cosHeight = (sourceBitmap.PixelHeight * Math.Cos(radius));
				targetWidth = (int)(Math.Abs(cosWidth) + Math.Abs(sinHeight) + 0.5);
				targetHeight = (int)(Math.Abs(sinWidth) + Math.Abs(cosHeight) + 0.5);
				if (angle > 0 && angle < 90)
				{
					gdiDrawingPoints = new Gdi.PointF[]{
						new Gdi.PointF((float)sinHeight, 0),
						new Gdi.PointF((float)(cosWidth + sinHeight), (float)sinWidth),
						new Gdi.PointF(0, (float)cosHeight),
					};
				}
				else if (angle > 90 && angle < 180)
				{
					gdiDrawingPoints = new Gdi.PointF[]{
						new Gdi.PointF(targetWidth, (float)-cosHeight),
						new Gdi.PointF((float)sinHeight, targetHeight),
						new Gdi.PointF((float)-cosWidth, 0),
					};
				}
				else if (angle > -90)
				{
					gdiDrawingPoints = new Gdi.PointF[]{
						new Gdi.PointF(0, (float)-sinWidth),
						new Gdi.PointF((float)cosWidth, 0),
						new Gdi.PointF((float)-sinHeight, targetHeight),
					};
				}
				else
				{
					gdiDrawingPoints = new Gdi.PointF[]{
						new Gdi.PointF((float)-cosWidth, targetHeight),
						new Gdi.PointF(0, (float)-cosHeight),
						new Gdi.PointF(targetWidth, (float)-sinWidth),
					};
				}
				if (preserveImageSize)
				{
					int paddingLeft = ((targetWidth - sourceBitmap.PixelWidth) / 2);
					int paddingTop = ((targetHeight - sourceBitmap.PixelHeight) / 2);
					targetWidth = sourceBitmap.PixelWidth;
					targetHeight = sourceBitmap.PixelHeight;
					gdiDrawingPoints[0].X -= paddingLeft;
					gdiDrawingPoints[0].Y -= paddingTop;
					gdiDrawingPoints[1].X -= paddingLeft;
					gdiDrawingPoints[1].Y -= paddingTop;
					gdiDrawingPoints[2].X -= paddingLeft;
					gdiDrawingPoints[2].Y -= paddingTop;
				}
			}

			//旋轉影像
			Gdi.Bitmap gdiSrcBitmap = null;
			Gdi.Bitmap gdiResultBitmap = null;
			try
			{
				//旋轉影像
				if (!_PreserveImageSize && angle == 90)
				{
					gdiResultBitmap = sourceImage.ToGdiBitmap();
					gdiResultBitmap.RotateFlip(Gdi.RotateFlipType.Rotate90FlipNone);
				}
				else if (!_PreserveImageSize && angle == -90)
				{
					gdiResultBitmap = sourceImage.ToGdiBitmap();
					gdiResultBitmap.RotateFlip(Gdi.RotateFlipType.Rotate270FlipNone);
				}
				else if (angle == 180 || angle == -180)
				{
					gdiResultBitmap = sourceImage.ToGdiBitmap();
					gdiResultBitmap.RotateFlip(Gdi.RotateFlipType.Rotate180FlipNone);
				}
				else
				{
					//旋轉影像
					gdiSrcBitmap = sourceBitmap.ToGdiBitmap();
					gdiResultBitmap = new Gdi.Bitmap(targetWidth, targetHeight, Gdi.Imaging.PixelFormat.Format32bppArgb);
					using (Gdi.Graphics graphics = Gdi.Graphics.FromImage(gdiResultBitmap))
					{
						//填充背景色
						if (backColor.A != 0)
						{
							using (Gdi.SolidBrush brush = new Gdi.SolidBrush(Gdi.Color.FromArgb(backColor.A, backColor.R, backColor.G, backColor.B)))
								graphics.FillRectangle(brush, 0, 0, targetWidth, targetHeight);
						}

						//繪製旋轉影像
						graphics.InterpolationMode = Gdi.Drawing2D.InterpolationMode.HighQualityBilinear;
						graphics.SmoothingMode = Gdi.Drawing2D.SmoothingMode.HighQuality;
						graphics.DrawImage(gdiSrcBitmap, gdiDrawingPoints);
					}

					//複製解析度
					gdiResultBitmap.SetResolution(gdiSrcBitmap.HorizontalResolution, gdiSrcBitmap.VerticalResolution);
				}

				//轉成 BitmapSource
				BitmapSource resultBitmapSource = gdiResultBitmap.ToBitmapSource();
				resultImage = new BitmapImage(sourceImage.MediaStorage, resultBitmapSource, true, sourceImage.Metadata, sourceImage.RawFormat);
			}
			finally
			{
				if (gdiSrcBitmap != null)
					gdiSrcBitmap.Dispose();
				if (gdiResultBitmap != null)
					gdiResultBitmap.Dispose();
			}

			//完成
			return true;
		}
        #endregion
		#region BackgroundColor
		/// <summary>
		/// 取得或設定當原始影像無法填滿至結果時所使用的填充色彩，預設值為 Colors.Transparent。
		/// </summary>
		public Color BackgroundColor
		{
			get
			{
				return _BackgroundColor;
			}
			set
			{
				this.SetInitializingParameter<Color>(ref _BackgroundColor, value);
			}
		}
		#endregion
		#region PreserveImageSize
		/// <summary>
		/// 取得或設定旋轉後是否維持原本的影像大小，預設值為 False。
		/// </summary>
		public bool PreserveImageSize
		{
			get
			{
				return _PreserveImageSize;
			}
			set
			{
				this.SetInitializingParameter<bool>(ref _PreserveImageSize, value);
			}
		}
		#endregion
		#region ValidateInitParameters
		/// <summary>
		/// 驗證初始化參數是否正確。
		/// </summary>
		/// <returns>True 表示參數正確，False 表示錯誤。</returns>
		protected override bool ValidateInitParameters()
		{
			return (base.ValidateInitParameters() && this.SourceImage is IBitmapImage);
		}
		#endregion
	}
}
